package my.edu.model.bills.bound.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import my.edu.common.constant.MessageConstant;
import my.edu.common.domain.PageDTO;
import my.edu.common.domain.PageVO;
import my.edu.common.exception.BillsException;
import my.edu.common.utils.PageUtils;
import my.edu.constant.BillsType;
import my.edu.constant.BoundBusinessType;
import my.edu.constant.OrderNoConstant;
import my.edu.model.bills.bound.entity.OtherInboundOrderDetail;
import my.edu.model.bills.bound.entity.dto.InboundDTO;
import my.edu.model.bills.bound.entity.dto.InboundDetailDTO;
import my.edu.model.bills.bound.entity.dto.InboundOrderSearchDTO;
import my.edu.model.bills.bound.entity.vo.InboundDetailVO;
import my.edu.model.bills.bound.entity.vo.InboundVO;
import my.edu.model.bills.sales.entity.SalesOrder;
import my.edu.model.information.entity.CustomerReceivableBalanceDetail;
import my.edu.model.information.entity.VO.CustomerVO;
import my.edu.utils.ChildrenLoader;
import my.edu.utils.OrderNoUtil;
import my.edu.utils.OrderServiceUtil;
import my.edu.websocket.BillWebSocket;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import my.edu.model.bills.bound.entity.OtherInboundOrder;
import my.edu.model.bills.bound.mapper.OtherInboundOrderMapper;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;

@Service
public class OtherInboundOrderServiceImpl extends ServiceImpl<OtherInboundOrderMapper, OtherInboundOrder> implements IOtherInboundOrderService {

    @Autowired
    private OrderServiceUtil orderServiceUtil;
    @Autowired
    private IOtherInboundOrderDetailService otherInboundOrderDetailService;
    @Autowired
    private BoundBusinessType businessType;
    @Autowired
    private OrderNoConstant orderNoConstant;
    @Autowired
    private BillWebSocket billWebSocket;


    @Override
    @Transactional
    public void saveNew(InboundDTO inboundDTO) {
        checkDTO(inboundDTO);
        OtherInboundOrder otherInboundOrder = BeanUtil.copyProperties(inboundDTO, OtherInboundOrder.class, "inboundDetailDTOList");
        setField(otherInboundOrder, inboundDTO.getInboundDetailDTOList(), true);
        save(otherInboundOrder);
        otherInboundOrderDetailService.saveByDTO(inboundDTO.getInboundDetailDTOList(), otherInboundOrder);
        billWebSocket.sendToAllClient(BillsType.OTHERINBOUND, "单据类型：入库单;单据编号：" + otherInboundOrder.getOrderNo() + ";提交人：" + otherInboundOrder.getCreatedName());

    }

    private void setField(OtherInboundOrder otherInboundOrder, List<InboundDetailDTO> inboundDetailDTOList, boolean isNew) {
        if (otherInboundOrder.getOrderNo() == null || otherInboundOrder.getOrderNo().isEmpty()) {
            //设置No
            String orderNo = OrderNoUtil.generateOrderNo(orderNoConstant.getOtherInboundNo());
            otherInboundOrder.setOrderNo(orderNo);
        } else {
            //检查是否有重复no
            long count = count(Wrappers.<OtherInboundOrder>lambdaQuery().eq(OtherInboundOrder::getOrderNo, otherInboundOrder.getOrderNo()));
            if (isNew && count > 0) throw new BillsException(MessageConstant.ORDER_NO_EXISTS);
        }
        Integer quantity = 0;
        BigDecimal amount = BigDecimal.ZERO;
        for (InboundDetailDTO inboundDetailDTO : inboundDetailDTOList) {
            quantity += inboundDetailDTO.getQuantity();
            amount = amount.add(inboundDetailDTO.getInboundPrice().multiply(BigDecimal.valueOf(inboundDetailDTO.getQuantity())));
        }
        otherInboundOrder.setQuantity(quantity);
        otherInboundOrder.setAmount(amount);
        otherInboundOrder.setIsAudited(0);
    }

    private void checkDTO(InboundDTO inboundDTO) {
        orderServiceUtil.checkDTO(inboundDTO.getDeliveryMethod(), null, inboundDTO.getSalespersonId(), inboundDTO.getSupplierId(), null);
        List<String> boundBusinessType = businessType.getInboundBusinessType();
        boolean contains = boundBusinessType.contains(inboundDTO.getBusinessType());
        if (!contains) throw new BillsException(MessageConstant.BOUND_BUSINESS_TYPE_NOT_FOUND);
    }

    @Override
    @Transactional
    public void updateByDTO(InboundDTO inboundDTO) {
        OtherInboundOrder byId = getById(inboundDTO.getId());
        if (byId == null) throw new BillsException(MessageConstant.ORDER_NOT_EXIST);
        if (byId.getIsAudited() != null && byId.getIsAudited() == 1)
            throw new BillsException(MessageConstant.ISAUDIT_CAN_NOT_UPDATE);
        checkDTO(inboundDTO);
        BeanUtil.copyProperties(inboundDTO, byId, "inboundDetailDTOList");
        setField(byId, inboundDTO.getInboundDetailDTOList(), false);
        updateById(byId);
        otherInboundOrderDetailService.updateByDTO(inboundDTO.getInboundDetailDTOList(), byId);
        billWebSocket.sendToAllClient(BillsType.OTHERINBOUND, "更新;单据类型：入库单;单据编号：" + byId.getOrderNo() + ";提交人：" + byId.getUpdateName());

    }

    @Override
    @Transactional
    public void removeOrder(List<Long> longs) {
        List<OtherInboundOrder> otherInboundOrders = listByIds(longs);
        for (OtherInboundOrder otherInboundOrder : otherInboundOrders) {
            if (otherInboundOrder.getIsAudited() != null && otherInboundOrder.getIsAudited() == 1)
                throw new BillsException(otherInboundOrder.getOrderNo() + MessageConstant.ISAUDIT_CAN_NOT_DEL);
        }
        removeBatchByIds(otherInboundOrders);
        otherInboundOrderDetailService.removeByOrderId(longs);
    }

    @Override
    public InboundVO getOrderOne(Long id) {
        MPJLambdaWrapper<OtherInboundOrder> wrapper = new MPJLambdaWrapper<>();
        wrapper
                .selectAsClass(OtherInboundOrder.class, InboundVO.class)
                .selectCollection(OtherInboundOrderDetail.class, InboundVO::getInboundDetailVOList)
                .leftJoin(OtherInboundOrderDetail.class, OtherInboundOrderDetail::getOtherInboundOrderId, OtherInboundOrder::getId)
                .eq(OtherInboundOrder::getId, id);

        return baseMapper.selectJoinOne(InboundVO.class, wrapper);
    }

    @Override
    public PageVO<InboundVO> getOrderList(PageDTO page, InboundOrderSearchDTO searchDTO) {
        MPJLambdaWrapper<OtherInboundOrder> wrapper = new MPJLambdaWrapper<>();
        wrapper
                .selectAsClass(OtherInboundOrder.class, InboundVO.class)
                .like(searchDTO.getOrderNo() != null && !searchDTO.getOrderNo().isEmpty(),
                        OtherInboundOrder::getOrderNo, searchDTO.getOrderNo())
                .between(searchDTO.getBeginTime() != null,
                        OtherInboundOrder::getOrderDate, searchDTO.getBeginTime(), searchDTO.getEndTime())
                .eq(searchDTO.getBoundBusinessType() != null && !searchDTO.getBoundBusinessType().isEmpty(),
                        OtherInboundOrder::getBusinessType, searchDTO.getBoundBusinessType())
                .eq(searchDTO.getIsAudited() != null,
                        OtherInboundOrder::getIsAudited, searchDTO.getIsAudited())
                .eq(searchDTO.getSalespersonId() != null,
                        OtherInboundOrder::getSalespersonId, searchDTO.getSalespersonId())
                .eq(searchDTO.getAuditorId() != null,
                        OtherInboundOrder::getAuditorId, searchDTO.getAuditorId())
                .eq(searchDTO.getSupplierId() != null,
                        OtherInboundOrder::getSupplierId, searchDTO.getSupplierId())
                .eq(searchDTO.getDeliveryMethod() != null,
                        OtherInboundOrder::getDeliveryMethod, searchDTO.getDeliveryMethod())
                .orderByDesc(OtherInboundOrder::getId);
        Page<InboundVO> inboundVOPage = baseMapper.selectJoinPage(new Page<>(page.getPage(), page.getSize()), InboundVO.class, wrapper);
        if (CollUtil.isNotEmpty(inboundVOPage.getRecords())) {
            ChildrenLoader.loadAndFillDTO(
                    inboundVOPage.getRecords(),
                    InboundVO::getId,
                    ids -> otherInboundOrderDetailService.list(
                            Wrappers.<OtherInboundOrderDetail>lambdaQuery().in(OtherInboundOrderDetail::getOtherInboundOrderId, ids)),
                    InboundDetailVO::getOtherInboundOrderId,
                    InboundDetailVO.class,
                    InboundVO::setInboundDetailVOList);
        };

        return PageUtils.toPage(inboundVOPage);
    }
    @Override
    public void auditOrder(List<Long> longs) {
        updateAudit(longs, true);
    }

    private void updateAudit(List<Long> longs, boolean isAudit) {
        List<OtherInboundOrder> list = list(Wrappers.<OtherInboundOrder>lambdaQuery().in(OtherInboundOrder::getId, longs));
        for (OtherInboundOrder order : list) {
            if (isAudit) {
                if (order.getIsAudited() != null && order.getIsAudited() == 1)
                    throw new BillsException(order.getOrderNo() + MessageConstant.ISAUDITED);
            } else if (order.getIsAudited() != null && order.getIsAudited() == 0) {
                throw new BillsException(order.getOrderNo() + MessageConstant.NOTAUDITED);
            }
        }
        if (isAudit) {
            orderServiceUtil.setAuditStatus(this, longs, true);
            otherInboundOrderDetailService.addStockByOrderId(longs);
        } else {
            orderServiceUtil.setAuditStatus(this, longs, false);
            otherInboundOrderDetailService.subStockByOrderId(longs);
        }
    }

    @Override
    public void rauditOrder(List<Long> longs) {
        updateAudit(longs, false);
    }
}
